package multiply_sim;

   // parameter VERSION = "1.1";

   // typedef enum {ADD, SUB, MUL} opcodes_t;

   // typedef struct {
   // 	  logic [31:0] a, b;
   // 	  opcodes_t opcode;
   // } instruction_t;



   typedef struct  {
	  logic signed [2:0] booth_array[8:0];
      logic [15+1:0] pp[8:0];
      logic 	     S[8:0];
      logic 	     E[8:0];
      logic [3:0]    cols_sum[31:0]; // sum of each column
      logic [31:0]   result;	     // the actual result
   } pps_16x16_t;					 // Particial products  of 16x16 mode
   
   typedef struct 		 {
	  logic signed [2:0] booth_array[4:0];
      logic [7+1:0] 		 pp[4:0];
      logic 			 S[4:0];
      logic 			 E[4:0];
      logic [2:0] 		 cols_sum[15:0]; // sum of each column
      logic [15:0] 		 result;	     // the actual result
   } pps_8x8_t;					 // Particial products  of 8x8 mode
   
   typedef struct 		 {
	  logic signed [2:0] booth_array[2:0];
      logic [3+1:0] 		 pp[2:0];
      logic 			 S[2:0];
      logic 			 E[2:0];
      logic [1:0] 		 cols_sum[7:0]; // sum of each column
      logic [7:0] 		 result;	     // the actual result
   } pps_4x4_t;					 // Particial products  of 4x4 mode

   //utilities
`define print(v) $display(`"variable v = %h`", v)
   

   
   
   // function automatic [31:0] multiplier (input [31:0] a, b);
   // 	  // code for a custom multiplier goes here
   // 	  return a * b;
   // endfunction // multiplier


   function void test_print();
	  $display("@%0t: hello", $time);
   endfunction


   // function automatic void compare_array(
   // 					 const ref [] array[]
   // 					 ); // how to get the size ???
   //    //$display("array[0] = %x", array.size());
      
      
   // endfunction


   
   // function void print_state(...);
   // 	  $display("@%0t: state = %s", $time, cur_state.name());
   // endfunction // print_state

	  
      

      function automatic pps_16x16_t gen_16x16_pps( 
						    input logic [15:0] i_a, i_b, 
						    input logic        i_sign );
	 localparam WIDTH = 16;
	 localparam PPS_NUM = WIDTH/2 + 1;
	 localparam RESULT_WIDTH = WIDTH*2;
	 
	 // input [WIDTH-1:0] 	 i_a;
	 // input [WIDTH-1:0] 	 i_b;
	 // input  		 i_sign;

	 logic [WIDTH:0] 	 a;
	 logic [WIDTH+1:0] 	 b;
	 logic [WIDTH+2:0] 	 b_ext;
	 
	 logic signed [2:0] 	 booth_array[PPS_NUM-1:0];// 0,+-1,+-2
      
	 logic [WIDTH:0] 	 pp[PPS_NUM-1:0];
	 logic 			 S[PPS_NUM-1:0]; // the last is not used
	 logic 			 E[PPS_NUM-1:0]; // the last is not used
	 logic [3:0] 		 cols_sum[RESULT_WIDTH-1:0];// FIXME : the width should log(width)+1 ??
	 logic [RESULT_WIDTH-1:0] result;
	 
      	 
	 a = {i_sign & i_a[WIDTH-1], i_a};
	 b = {{2{i_sign & i_b[WIDTH-1]}}, i_b};
	 b_ext = {b, 1'b0};
      
	 // ==================== for 16x16 mode ====================
	 for(int i=0;i<=WIDTH;i+=2)	// total 9
	   begin
	      booth_array[i/2] = b_ext[i] + b_ext[i+1] - 2*b_ext[i+2]; // 0,+-1,+-2
	      //$display("booth_array[%d] = %d", i/2, booth_array[i/2]);
	   end
	 // gen PPs and S, E
	 for(int i=0;i<PPS_NUM;i++)
	   begin
	      case(booth_array[i])
		3'd0:
		  pp[i] = '0;
	  	3'd1:
	  	  pp[i] = a;
	  	3'd2:
	  	  pp[i] = a<<1;
	  	-3'd1:
	  		  pp[i] = ~a;
	  	-3'd2:
	  		  pp[i] = ~(a<<1);
	      endcase // case (booth_array[i])
	      //S (the same as the sign of booth_array[i])
	      S[i] = booth_array[i] < 0 ? 1'b1 : 1'b0;
	      //E
	      E[i] = (booth_array[i] == 3'd0) ? 1'b1 : S[i] ~^ a[WIDTH];
	   end // for (int i=0;i<PPS_NUM;i++)
	 	 

      // ==================== sum of each column (16x16 mode) ====================
      cols_sum[0] = pp[0][0] + S[0];
      cols_sum[1] = pp[0][1];
      cols_sum[2] = pp[0][2] + pp[1][0] + S[1];
      cols_sum[3] = pp[0][3] + pp[1][1];
      cols_sum[4] = pp[0][4] + pp[1][2] + pp[2][0] + S[2];
      cols_sum[5] = pp[0][5] + pp[1][3] + pp[2][1];
      cols_sum[6] = pp[0][6] + pp[1][4] + pp[2][2] + pp[3][0] + S[3];
      cols_sum[7] = pp[0][7] + pp[1][5] + pp[2][3] + pp[3][1];
      cols_sum[8] = pp[0][8] + pp[1][6] + pp[2][4] + pp[3][2] + pp[4][0] + S[4];
      cols_sum[9] = pp[0][9] + pp[1][7] + pp[2][5] + pp[3][3] + pp[4][1];
      cols_sum[10] = pp[0][10] + pp[1][8] + pp[2][6] + pp[3][4] + pp[4][2] + pp[5][0] + S[5];
      cols_sum[11] = pp[0][11] + pp[1][9] + pp[2][7] + pp[3][5] + pp[4][3] + pp[5][1];
      cols_sum[12] = pp[0][12] + pp[1][10] + pp[2][8] + pp[3][6] + pp[4][4] + pp[5][2] + pp[6][0] + S[6];
      cols_sum[13] = pp[0][13] + pp[1][11] + pp[2][9] + pp[3][7] + pp[4][5] + pp[5][3] + pp[6][1];
      cols_sum[14] = pp[0][14] + pp[1][12] + pp[2][10] + pp[3][8] + pp[4][6] + pp[5][4] + pp[6][2] + pp[7][0] + S[7];
      cols_sum[15] = pp[0][15] + pp[1][13] + pp[2][11] + pp[3][9] + pp[4][7] + pp[5][5] + pp[6][3] + pp[7][1];
      cols_sum[16] = pp[0][16] + pp[1][14] + pp[2][12] + pp[3][10] + pp[4][8] + pp[5][6] + pp[6][4] + pp[7][2] + pp[8][0];
      cols_sum[17] = {~E[0]} + pp[1][15] + pp[2][13] + pp[3][11] + pp[4][9] + pp[5][7] + pp[6][5] + pp[7][3] + pp[8][1];
      cols_sum[18] = {~E[0]} + pp[1][16] + pp[2][14] + pp[3][12] + pp[4][10] + pp[5][8] + pp[6][6] + pp[7][4] + pp[8][2];
      cols_sum[19] = E[0] + E[1] + pp[2][15] + pp[3][13] + pp[4][11] + pp[5][9] + pp[6][7] + pp[7][5] + pp[8][3];
      cols_sum[20] = 1'b1 + pp[2][16] + pp[3][14] + pp[4][12] + pp[5][10] + pp[6][8] + pp[7][6] + pp[8][4];
      cols_sum[21] = E[2] + pp[3][15] + pp[4][13] + pp[5][11] + pp[6][9] + pp[7][7] + pp[8][5];
      cols_sum[22] = 1'b1 + pp[3][16] + pp[4][14] + pp[5][12] + pp[6][10] + pp[7][8] + pp[8][6];
      cols_sum[23] = E[3] + pp[4][15] + pp[5][13] + pp[6][11] + pp[7][9] + pp[8][7];
      cols_sum[24] = 1'b1 + pp[4][16] + pp[5][14] + pp[6][12] + pp[7][10] + pp[8][8];
      cols_sum[25] = E[4] + pp[5][15] + pp[6][13] + pp[7][11] + pp[8][9];
      cols_sum[26] = 1'b1 + pp[5][16] + pp[6][14] + pp[7][12] + pp[8][10];
      cols_sum[27] = E[5] + pp[6][15] + pp[7][13] + pp[8][11];
      cols_sum[28] = 1'b1 + pp[6][16] + pp[7][14] + pp[8][12];
      cols_sum[29] = E[6] + pp[7][15] + pp[8][13];
      cols_sum[30] = 1'b1 + pp[7][16] + pp[8][14];
      cols_sum[31] = E[7] + pp[8][15];
      // ==================== sum of each column ====================

      // the result
	 result = '0;
	 for(int i=0;i<RESULT_WIDTH;i++)
	   result += cols_sum[i]<<i;

	 //the return value
		 gen_16x16_pps.booth_array = booth_array;
	 gen_16x16_pps.pp = pp;
	 gen_16x16_pps.S = S;
	 gen_16x16_pps.E = E;
	 gen_16x16_pps.cols_sum = cols_sum;
	 gen_16x16_pps.result = result;
         
   endfunction // gen_pps
   
   
   function automatic pps_8x8_t gen_8x8_pps( 
					     input logic [7:0] i_a, i_b, 
					     input logic       i_sign );
      localparam WIDTH = 8;
      localparam PPS_NUM = WIDTH/2 + 1;
      localparam RESULT_WIDTH = WIDTH*2;
	 
   	 // input logic [WIDTH-1:0] i_a, i_b;
   	 // input logic 		 i_sign;

   	 logic [WIDTH:0] 	 a;
   	 logic [WIDTH+1:0] 	 b;
   	 logic [WIDTH+2:0] 	 b_ext;
	 
   	 logic signed [2:0] 	 booth_array[PPS_NUM-1:0];// 0,+-1,+-2
      
   	 logic [WIDTH:0] 	 pp[PPS_NUM-1:0];
   	 logic 			 S[PPS_NUM-1:0]; // the last is not used
   	 logic 			 E[PPS_NUM-1:0]; // the last is not used
   	 logic [2:0] 		 cols_sum[RESULT_WIDTH-1:0]; // FIXME : the width should log(width)+1 ??
   	 logic [RESULT_WIDTH-1:0] result;
	 
      	 
   	 a = {i_sign & i_a[WIDTH-1], i_a};
   	 b = {{2{i_sign & i_b[WIDTH-1]}}, i_b};
   	 b_ext = {b, 1'b0};
      
   	 // ==================== for 8x8 mode ====================
   	 for(int i=0;i<=WIDTH;i+=2)	// total 9
   	   begin
   	      booth_array[i/2] = b_ext[i] + b_ext[i+1] - 2*b_ext[i+2]; // 0,+-1,+-2
   	      //$display("booth_array[%d] = %d", i/2, booth_array[i/2]);
   	   end
   	 // gen PPs and S, E
   	 for(int i=0;i<PPS_NUM;i++)
   	   begin
   	      case(booth_array[i])
   		3'd0:
   		  pp[i] = '0;
   	  	3'd1:
   	  	  pp[i] = a;
   	  	3'd2:
   	  	  pp[i] = a<<1;
   	  	-3'd1:
   	  		  pp[i] = ~a;
   	  	-3'd2:
   	  		  pp[i] = ~(a<<1);
   	      endcase // case (booth_array[i])
   	      //S (the same as the sign of booth_array[i])
   	      S[i] = booth_array[i] < 0 ? 1'b1 : 1'b0;
   	      //E
   	      E[i] = (booth_array[i] == 3'd0) ? 1'b1 : S[i] ~^ a[WIDTH];
   	   end // for (int i=0;i<PPS_NUM;i++)
	 	 

      // ==================== sum of each column (8x8 mode) ====================
      cols_sum[0] = pp[0][0] + S[0];
      cols_sum[1] = pp[0][1];
      cols_sum[2] = pp[0][2] + pp[1][0] + S[1];
      cols_sum[3] = pp[0][3] + pp[1][1];
      cols_sum[4] = pp[0][4] + pp[1][2] + pp[2][0] + S[2];
      cols_sum[5] = pp[0][5] + pp[1][3] + pp[2][1];
      cols_sum[6] = pp[0][6] + pp[1][4] + pp[2][2] + pp[3][0] + S[3];
      cols_sum[7] = pp[0][7] + pp[1][5] + pp[2][3] + pp[3][1];
      cols_sum[8] = pp[0][8] + pp[1][6] + pp[2][4] + pp[3][2] + pp[4][0];
      cols_sum[9] = {~E[0]} + pp[1][7] + pp[2][5] + pp[3][3] + pp[4][1];
      cols_sum[10] = {~E[0]} + pp[1][8] + pp[2][6] + pp[3][4] + pp[4][2];
      cols_sum[11] = E[0] + E[1] + pp[2][7] + pp[3][5] + pp[4][3];
      cols_sum[12] = 1'b1 + pp[2][8] + pp[3][6] + pp[4][4];
      cols_sum[13] = E[2] + pp[3][7] + pp[4][5];
      cols_sum[14] = 1'b1 + pp[3][8] + pp[4][6];
      cols_sum[15] = E[3] + pp[4][7];
      // ==================== sum of each column ====================

      // the result
   	 result = '0;
   	 for(int i=0;i<RESULT_WIDTH;i++)
   	   result += cols_sum[i]<<i;

   	 //the return value
	  gen_8x8_pps.booth_array = booth_array;
   	 gen_8x8_pps.pp = pp;
   	 gen_8x8_pps.S = S;
   	 gen_8x8_pps.E = E;
   	 gen_8x8_pps.cols_sum = cols_sum;
   	 gen_8x8_pps.result = result;
         
   endfunction


   function automatic pps_4x4_t gen_4x4_pps( 
					     input logic [3:0] i_a, i_b, 
					     input logic       i_sign );
      localparam WIDTH = 4;
      localparam PPS_NUM = WIDTH/2 + 1;
      localparam RESULT_WIDTH = WIDTH*2;
	 
      // input logic [WIDTH-1:0] i_a, i_b;
      // input logic 		 i_sign;
      
      logic [WIDTH:0] 					       a;
      logic [WIDTH+1:0] 				       b;
      logic [WIDTH+2:0] 				       b_ext;
      
      logic signed [2:0] 				       booth_array[PPS_NUM-1:0];// 0,+-1,+-2
      
      logic [WIDTH:0] 					       pp[PPS_NUM-1:0];
      logic 						       S[PPS_NUM-1:0]; // the last is not used
      logic 						       E[PPS_NUM-1:0]; // the last is not used
      logic [1:0] 					       cols_sum[RESULT_WIDTH-1:0]; // FIXME : the width should log(width)+1 ??
      logic [RESULT_WIDTH-1:0] 				       result;
      
      a = {i_sign & i_a[WIDTH-1], i_a};
      b = {{2{i_sign & i_b[WIDTH-1]}}, i_b};
      b_ext = {b, 1'b0};
      
      // ==================== for 4x4 mode ====================
      for(int i=0;i<=WIDTH;i+=2)	// total 9
   	begin
   	   booth_array[i/2] = (b_ext[i] + b_ext[i+1] - 2*b_ext[i+2]); // 0,+-1,+-2
   	   //$display("booth_array[%d] = %d", i/2, booth_array[i/2]);
   	end
      // gen PPs and S, E
      for(int i=0;i<PPS_NUM;i++)
   	begin
   	   case(booth_array[i])
   	     3'd0:
   	       pp[i] = '0;
   	     3'd1:
   	       pp[i] = a;
   	     3'd2:
   	       pp[i] = a<<1;
   	     -3'd1:
   	  	       pp[i] = ~a;
   	     -3'd2:
   	  	       pp[i] = ~(a<<1);
   	   endcase // case (booth_array[i])
   	   //S (the same as the sign of booth_array[i])
   	   S[i] = booth_array[i] < 0 ? 1'b1 : 1'b0;
   	   //E
   	   E[i] = (booth_array[i] == 3'd0) ? 1'b1 : S[i] ~^ a[WIDTH];
   	end // for (int i=0;i<PPS_NUM;i++)
	 	 

      // ==================== sum of each column (4x4 mode) ====================
      cols_sum[0] = pp[0][0] + S[0];
      cols_sum[1] = pp[0][1];
      cols_sum[2] = pp[0][2] + pp[1][0] + S[1];
      cols_sum[3] = pp[0][3] + pp[1][1];
      cols_sum[4] = pp[0][4] + pp[1][2] + pp[2][0];
      cols_sum[5] = {~E[0]} + pp[1][3] + pp[2][1];
      cols_sum[6] = {~E[0]} + pp[1][4] + pp[2][2];
      cols_sum[7] = E[0] + E[1] + pp[2][3];
      // ==================== sum of each column ====================

      // the result
      result = '0;
      for(int i=0;i<RESULT_WIDTH;i++)
   	result += cols_sum[i]<<i;
      
      //the return value
	  gen_4x4_pps.booth_array = booth_array;
      gen_4x4_pps.pp = pp;
      gen_4x4_pps.S = S;
      gen_4x4_pps.E = E;
      gen_4x4_pps.cols_sum = cols_sum;
      gen_4x4_pps.result = result;
      
   endfunction
   
   
endpackage // definitions
   
   